import {
  Component,
  computed,
  defineComponent,
  onMounted,
  ref,
  VNode,
} from 'vue';
import { props } from './constant';
import SearchForm from './SearchForm/index.vue';
import { Action } from './interface';
import {
  Button,
  Pagination,
  Space,
  Table,
  Flex,
  TablePaginationConfig,
  TableProps,
  ConfigProvider,
} from 'ant-design-vue';
import { useConfigInject } from '../../ConfigProvide/index';
import { usePagination } from 'vue-request';
import './index.scss';
import { useElementSize } from '@vueuse/core';

export const BoTable = defineComponent({
  name: 'BoTable',
  props,
  setup(props, { slots, expose }) {
    const { configProvide } = useConfigInject();
    const renderStatus = computed(() => configProvide.renderStatus);

    const searchParams = ref({});
    const service = async (params: any) => {
      const res = await props.request({ ...params, ...searchParams.value });
      return {
        rows: res!.rows,
        total: res!.total || res!.rows?.length,
      };
    };
    const { data, current, pageSize, loading, total, changePagination } =
      usePagination(service, {
        manual: true,
        pagination: {
          totalKey: 'total',
          currentKey: 'pageNum',
          pageSizeKey: 'pageSize',
        },
      });
    const dataSource = computed(() => {
      return data.value?.rows || [];
    });
    const formRef = ref();
    const resetPage = ref(true);
    const onSearch = (params: Record<string, any>) => {
      searchParams.value = params;
      changePagination(resetPage.value ? 1 : current.value, pageSize.value);
    };

    const search = (params: Record<string, any>, _resetPage = true) => {
      resetPage.value = _resetPage;
      formRef.value.search(params);
    };
    const reset = (resetPage = true) => {
      if (props.hideSearch) {
        changePagination(resetPage ? 1 : current.value, pageSize.value);
      } else {
        formRef.value.reset();
      }
    };

    const refresh = () => {
      props.hideSearch
        ? changePagination(1, pageSize.value)
        : formRef.value.reset();
    };

    expose({
      search,
      reset,
      refresh,
    });

    onMounted(() => {
      if (props.autoload) {
        changePagination(current.value, Number(props.pageSizeOptions![0]));
      }
    });

    const bodyCell = (scope: { column: { dataIndex: string | number } }) => {
      return slots[scope.column.dataIndex]?.(scope);
    };

    const actionsWidth = ref(50);
    const _columns = computed(() => {
      const columns = props.columns?.reduce<any[]>((prev, item) => {
        if (item.hideInTable) return prev;
        // 渲染操作区
        if (item.actions) {
          item.width = actionsWidth.value + 32;
          item.customRender = ({ text, record, index, renderIndex, value }) => {
            let _actions: Action[] = [];
            if (typeof item.actions === 'function') {
              _actions = item.actions({
                text,
                record,
                index,
                renderIndex,
                value,
              });
            } else {
              _actions = item.actions || [];
            }
            return (
              <Space
                size={2}
                onVnodeMounted={({ el }) => {
                  el!.clientWidth > actionsWidth.value &&
                    (actionsWidth.value = el!.clientWidth);
                }}
              >
                {_actions.reduce<VNode[]>((prev, action) => {
                  if (action.hidden) return prev;
                  prev.push(
                    <Button
                      type={action.type || 'link'}
                      onClick={() => action.onClick?.(record)}
                      icon={action.icon}
                      disabled={
                        typeof action.disabled === 'function'
                          ? action.disabled?.(record)
                          : action.disabled
                      }
                    >
                      {action.text}
                    </Button>,
                  );
                  return prev;
                }, [])}
              </Space>
            );
          };
        }
        // 渲染valueEnum区域
        if (item.valueEnum) {
          item.customRender = (opt) => {
            const result = (item.valueEnum || {})[
              opt.text as keyof typeof item.valueEnum
            ];
            if (item.valueType === 'status') {
              return renderStatus.value(result || {});
            }

            return item.valueEnum![opt.value as keyof typeof item.valueEnum]
              ?.text;
          };
        }
        prev.push({
          ...item,
          align: item.align || props.align,
          width: item.width || 200,
        });

        return prev;
      }, []);
      if (props.showIndex) {
        const fixed =
          typeof props.showIndex === 'boolean' ? false : props.showIndex.fixed;

        columns.unshift({
          title: '序号',
          fixed,
          width: 80,
          align: 'center',
          dataIndex: '_index',
          customRender(opt: { index: number }) {
            return (current.value - 1) * pageSize.value + opt.index + 1;
          },
        });
      }

      return columns;
    });

    const pagination = computed<TablePaginationConfig>(() => {
      return {
        total: total.value,
        current: current.value,
        pageSize: pageSize.value,
        pageSizeOptions: props.pageSizeOptions,
        onChange: changePagination,
        showTotal: (total) => `共${total}条`,
        showSizeChanger: true,
        showQuickJumper: true,
      };
    });

    const tableWrapRef = ref();
    const { height: tableWrapHeight } = useElementSize(tableWrapRef);

    const tableHeaderHeight = ref(0);
    const tableBodyHeight = computed(() => {
      return tableWrapHeight.value - tableHeaderHeight.value;
    });

    const onTableUpdated = (el: HTMLElement) => {
      const tableHeight = el.querySelector('.ant-table-thead');
      tableHeaderHeight.value = tableHeight?.clientHeight || 0;
    };

    const customTableProps = computed<TableProps>(() => {
      const scroll: Record<string, any> = {
        x: 500,
      };
      const style: Record<string, any> = {};

      if (props.height === 'auto') {
        return {
          scroll,
        };
      } else if (typeof props.height === 'number') {
        const h = props.height - tableHeaderHeight.value;
        scroll.y = h;
        style['--bo-table-height'] = h + 'px';
      } else {
        scroll.y = tableBodyHeight.value;
        style['--bo-table-height'] = tableBodyHeight.value + 'px';
      }

      return {
        scroll,
        style,
      };
    });

    const _Table = computed<Component>(() => (
      <Table
        {...props}
        {...customTableProps.value}
        columns={_columns.value}
        dataSource={dataSource.value}
        loading={loading.value}
        pagination={false}
        v-slots={{
          ...slots,
          bodyCell: slots.bodyCell ?? bodyCell,
        }}
        onVnodeUpdated={({ el }) => onTableUpdated(el as HTMLElement)}
      ></Table>
    ));

    return {
      onSearch,
      bodyCell,
      dataSource,
      Table: _Table,
      formRef,
      tableWrapRef,
      pagination,
      configProvide,
    };
  },
  render() {
    return (
      <div class='bo-table'>
        {this.$slots.leftContainer?.()}
        <div class='bo-table_container'>
          {this.hideSearch ? (
            <></>
          ) : (
            <div class='bo-table_search'>
              <ConfigProvider {...this.configProvide.searchConfigProvider}>
                <SearchForm
                  ref='formRef'
                  columns={this.columns}
                  onSearch={this.onSearch}
                />
              </ConfigProvider>
            </div>
          )}

          <div class='bo-table_toolbar'>
            <ConfigProvider {...this.configProvide.toolbarsConfigProvider}>
              {this.$slots.toolbars?.()}
            </ConfigProvider>
          </div>
          <div class='bo-table_wrap' ref='tableWrapRef'>
            {this.$slots.customTable?.({
              columns: this.columns,
              data: this.dataSource,
              Table: this.Table,
            }) || this.Table}
          </div>
          <Flex justify='flex-end'>
            {!this.hidePagination && <Pagination {...this.pagination} />}
          </Flex>
        </div>
      </div>
    );
  },
});
